home *** CD-ROM | disk | FTP | other *** search
- Subject: v06i090: Sun RPC Source (rpc2), Part02/11
- Newsgroups: mod.sources
- Approved: rs@mirror.UUCP
-
- Submitted by: sun!ferne!marks (Mark Stein)
- Mod.sources: Volume 6, Issue 90
- Archive-name: rpc2/Part02
-
- [ All I have done is verify that the shar files unpack correctly. -r$ ]
-
- Sun RPC source (part 2 of 11). This software package contains code
- and documentation for Revision 3.0 of the Sun Remote Procedure Call
- library. In addition, a beta version of the XDR/RPC protocol compiler
- is included. Comments about this latest release may be mailed to
- sun!rpc or rpc@sun.com.
-
- Sun RPC is a product of Sun Microsystems, Inc. and is provided for
- unrestricted use provided that this legend is included on all tape
- media and as a part of the software program in whole or part. Users
- may copy or modify Sun RPC without charge, but are not authorized to
- license or distribute it to anyone else except as part of a product or
- program developed by the user.
-
- - - - - - - - - - C U T - H E R E - - - - - - - - - - - - - - - - - -
- #! /bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create:
- # rpc/doc/rpc.prog.p1
- # This archive created: Mon Jul 14 16:54:58 1986
- export PATH; PATH=/bin:/usr/bin:$PATH
- for d in rpc rpc/doc rpc/rpclib rpc/tools rpc/toys rpc/rpclib/profiled rpc/rpcgen rpc/rpcgen/test
- do
- if test ! -d $d
- then
- echo "shar: Making directory $d"
- mkdir $d
- chmod 755 $d
- fi
- done
- echo shar: "extracting 'rpc/doc/rpc.prog.p1'" '(49365 characters)'
- if test -f 'rpc/doc/rpc.prog.p1'
- then
- echo shar: "will not over-write existing file 'rpc/doc/rpc.prog.p1'"
- else
- sed 's/^X//' << \SHAR_EOF > 'rpc/doc/rpc.prog.p1'
- X.PL RIGHT
- X.TL
- XRemote Procedure Call
- X.br
- XProgramming Guide
- X.bp
- X.NH
- XIntroduction
- X.LP
- XThis document is intended for programmers
- Xwho wish to write network applications
- Xusing remote procedure calls (explained below),
- Xthus avoiding low-level system primitives based on sockets.
- XThe reader must be familiar with the C programming language,
- Xand should have a working knowledge of network theory.
- X.LP
- XPrograms that communicate over a network
- Xneed a paradigm for communication.
- XA low-level mechanism might
- Xsend a signal on the arrival of incoming packets,
- Xcausing a network signal handler to execute.
- XA high-level mechanism would be the Ada
- X.LW rendezvous .
- XThe method used at Sun is the
- XRemote Procedure Call (RPC) paradigm,
- Xin which a client communicates with a server.
- XIn this process,
- Xthe client first calls a procedure to send a data packet to the server.
- XWhen the packet arrives, the server calls a dispatch routine,
- Xperforms whatever service is requested, sends back the reply,
- Xand the procedure call returns to the client.
- X.NH 2
- XLayers of RPC
- X.LP
- XThe RPC interface is divided into three layers.
- XThe highest layer is totally transparent to the programmer.
- XTo illustrate,
- Xat this level a program can contain a call to
- X.LW rnusers() ,
- Xwhich returns the number of users on a remote machine.
- XYou don't have to be aware that RPC is being used,
- Xsince you simply make the call in a program,
- Xjust as you would call
- X.LW malloc() .
- X.LP
- XAt the middle layer, the routines
- X.LW registerrpc()
- Xand
- X.LW callrpc()
- Xare used to make RPC calls:
- X.LW registerrpc()
- Xobtains a unique system-wide number, while
- X.LW callrpc()
- Xexecutes a remote procedure call.
- XThe
- X.LW rnusers()
- Xcall is implemented using these two routines.
- XThe middle-layer routines are designed for most common applications,
- Xand shield the user from knowing about sockets.
- X.LP
- XThe lowest layer is for more sophisticated applications,
- Xsuch as altering the defaults of the routines.
- XAt this layer, you can explicitly manipulate
- Xsockets that transmit RPC messages.
- XThis level should be avoided if possible.
- X.LP
- XSection 2 of this manual illustrates use of the highest two layers
- Xwhile Section 3 presents the low-level interface.
- XSection 4 of the manual discusses miscellaneous topics.
- XThe final section summarizes
- Xall the entry points into the RPC system.
- X.LP
- XAlthough this document only discusses the interface to C,
- Xremote procedure calls can be made from any language.
- XEven though this document discusses RPC
- Xwhen it is used to communicate
- Xbetween processes on different machines,
- Xit works just as well for communication
- Xbetween different processes on the same machine.
- X.NH 2
- XThe RPC Paradigm
- X.LP
- XHere is a diagram of the RPC paradigm:
- X.LP
- X.PL FULL
- X.PS
- XL1: arrow down 1i "client " rjust "program " rjust
- XL2: line right 1.5i "\fLcallrpc()\fP" "function"
- Xmove up 1.5i; line dotted down 6i; move up 4.5i
- Xarrow right 1i
- XL3: arrow down 1i "execute " rjust "request " rjust
- XL4: arrow right 1.5i "call" "service"
- XL5: arrow down 1i " service" ljust " executes" ljust
- XL6: arrow left 1.5i "\fLreturn\fP" "answer"
- XL7: arrow down 1i "request " rjust "completed " rjust
- XL8: line left 1i
- Xarrow left 1.5i "\fLreturn\fP" "reply"
- XL9: arrow down 1i "program " rjust "continues " rjust
- Xline dashed down from L2 to L9
- Xline dashed down from L4 to L7
- Xline dashed up 1i from L3 "service " rjust "daemon " rjust
- Xarrow dashed down 1i from L8
- Xmove right 1i from L3
- Xbox invis "Machine B"
- Xmove left 1.2i from L2; move down
- Xbox invis "Machine A"
- X.PE
- X.FN "Network Communication with the Remote Procedure Call"
- X.PL RIGHT
- X.bp
- X.NH
- XHigher Layers of RPC
- X.NH 2
- XHighest Layer
- X.LP
- XImagine you're writing a program that needs to know
- Xhow many users are logged into a remote machine.
- XYou can do this by calling the library routine
- X.LW rnusers() ,
- Xas illustrated below:
- X.BS
- X.LS
- X#include <stdio.h>
- X.sp.5
- Xmain(argc, argv)
- X int argc;
- X char **argv;
- X{
- X unsigned num;
- X.sp.5
- X if (argc < 2) {
- X fprintf(stderr, "usage: rnusers hostname\en");
- X exit(1);
- X }
- X if ((num = rnusers(argv[1])) < 0) {
- X fprintf(stderr, "error: rnusers\en");
- X exit(-1);
- X }
- X printf("%d users on %s\en", num, argv[1]);
- X exit(0);
- X}
- X.Lf
- X.BE
- XRPC library routines such as
- X.LW rnusers()
- Xare in the RPC services library
- X.LW librpcsvc.a .
- XThus, the program above should be compiled with
- X.BS
- X.LS
- X% cc \fIprogram\fP.c -lrpcsvc
- X.Lf
- X.BE
- XThis routine, and other RPC library routines,
- Xare documented in section 3R of the
- X.I "System Interface Manual for the Sun Workstation" .
- XHere is a table of RPC service library routines
- Xavailable to the C programmer:
- X.TN "RPC Service Library Routines"
- X.TS
- Xbox;
- XcfBI s
- Xc c
- XlfL l.
- X.sp.5
- X\s+2RPC Service Library Routines\s-2
- X.sp.5
- X_
- X\fIroutine description\fP
- X_
- Xrnusers() return number of users on remote machine
- Xrusers() return information about users on remote machine
- Xhavedisk() determine if remote machine has disk
- Xrstat() get performance data from remote kernel
- Xrwall() write to specified remote machines
- Xgetmaster() get name of YP master
- Xgetrpcport() get RPC port number
- Xyppasswd() update user password in yellow pages
- X.TE
- X.LP
- XThe other RPC services \(em
- X.LW ether ,
- X.LW mount ,
- X.LW rquota ,
- Xand
- X.LW spray
- X\(em are not available to the C programmer as library routines.
- XThey do, however,
- Xhave RPC program numbers so they can be invoked with
- X.LW callrpc() ,
- Xwhich will be discussed in the next section.
- X.bp
- X.NH 2
- XIntermediate Layer
- X.LP
- XThe simplest interface, which explicitly makes RPC
- Xcalls, uses the functions
- X.LW callrpc()
- Xand
- X.LW registerrpc() .
- XUsing this method, another way to get the number of remote users is:
- X.BS
- X.LS
- X#include <stdio.h>
- X#include <rpcsvc/rusers.h>
- X.sp.5
- Xmain(argc, argv)
- X int argc;
- X char **argv;
- X{
- X unsigned long nusers;
- X.sp.5
- X if (argc < 2) {
- X fprintf(stderr, "usage: nusers hostname\en");
- X exit(-1);
- X }
- X if (callrpc(argv[1],
- X RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
- X xdr_void, 0, xdr_u_long, &nusers) != 0) {
- X fprintf(stderr, "error: callrpc\en");
- X exit(1);
- X }
- X printf("%d users on %s\en", nusers, argv[1]);
- X exit(0);
- X}
- X.Lf
- X.BE
- XA program number, version number, and procedure number
- Xdefines each RPC procedure.
- XThe program number defines a group
- Xof related remote procedures, each of which has a different
- Xprocedure number.
- XEach program also has a version number,
- Xso when a minor change is made to a remote service
- X(adding a new procedure, for example),
- Xa new program number doesn't have to be assigned.
- XWhen you want to call a procedure to
- Xfind the number of remote users, you look up the appropriate
- Xprogram, version and procedure numbers
- Xin a manual, similar to when you look up the name of memory
- Xallocator when you want to allocate memory.
- X.LP
- XThe simplest routine in the RPC library
- Xused to make remote procedure calls is
- X.LW callrpc() .
- XIt has eight parameters.
- XThe first is the name of the remote machine.
- XThe next three parameters
- Xare the program, version, and procedure numbers.
- XThe following two parameters
- Xdefine the argument of the RPC call, and the final two parameters
- Xare for the return value of the call.
- XIf it completes successfully,
- X.LW callrpc()
- Xreturns zero, but nonzero otherwise.
- XThe exact meaning of the return codes is found in
- X.LW <rpc/clnt.h> ,
- Xand is in fact an
- X.LW "enum clnt_stat"
- Xcast into an integer.
- X.LP
- XSince data types may be represented differently on different machines,
- X.LW callrpc()
- Xneeds both the type of the RPC argument, as well as
- Xa pointer to the argument itself (and similarly for the result). For
- X.LW RUSERSPROC_NUM ,
- Xthe return value is an
- X.LW "unsigned long" ,
- Xso
- X.LW callrpc()
- Xhas
- X.LW xdr_u_long
- Xas its first return parameter, which says
- Xthat the result is of type
- X.LW "unsigned long" ,
- Xand
- X.LW &nusers
- Xas its second return parameter,
- Xwhich is a pointer to where the long result will be placed. Since
- X.LW RUSERSPROC_NUM
- Xtakes no argument, the argument parameter of
- X.LW callrpc()
- Xis
- X.LW xdr_void .
- X.LP
- XAfter trying several times to deliver a message, if
- X.LW callrpc()
- Xgets no answer, it returns with an error code.
- XThe delivery mechanism is UDP,
- Xwhich stands for User Datagram Protocol.
- XMethods for adjusting the number of retries
- Xor for using a different protocol require you to use the lower
- Xlayer of the RPC library, discussed later in this document.
- XThe remote server procedure
- Xcorresponding to the above might look like this:
- X.BS
- X.LS
- Xchar *
- Xnuser(indata)
- X char *indata;
- X{
- X static int nusers;
- X.sp.5
- X /*
- X * code here to compute the number of users
- X * and place result in variable nusers
- X */
- X return((char *)&nusers);
- X}
- X.Lf
- X.BE
- X.LP
- XIt takes one argument, which is a pointer to the input
- Xof the remote procedure call (ignored in our example),
- Xand it returns a pointer to the result.
- XIn the current version of C,
- Xcharacter pointers are the generic pointers,
- Xso both the input argument and the return value are cast to
- X.LW "char *" .
- X.LP
- XNormally, a server registers all of the RPC calls it plans
- Xto handle, and then goes into an infinite loop waiting to service requests.
- XIn this example, there is only a single procedure
- Xto register, so the main body of the server would look like this:
- X.BS
- X.LS
- X#include <stdio.h>
- X#include <rpcsvc/rusers.h>
- X.sp.5
- Xchar *nuser();
- X.sp.5
- Xmain()
- X{
- X registerrpc(RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
- X nuser, xdr_void, xdr_u_long);
- X svc_run(); /* never returns */
- X fprintf(stderr, "Error: svc_run returned!\en");
- X exit(1);
- X}
- X.Lf
- X.BE
- X.LP
- XThe
- X.LW registerrpc()
- Xroutine establishes what C procedure
- Xcorresponds to each RPC procedure number.
- XThe first three parameters,
- X.LW RUSERPROG ,
- X.LW RUSERSVERS ,
- Xand
- X.LW RUSERSPROC_NUM
- Xare the program, version, and procedure numbers
- Xof the remote procedure to be registered;
- X.LW nuser()
- Xis the name of the C procedure implementing it;
- Xand
- X.LW xdr_void
- Xand
- X.LW xdr_u_long
- Xare the types of the input to and output from the procedure.
- X.LP
- XOnly the UDP transport mechanism can use
- X.LW registerrpc() ;
- Xthus, it is always safe in conjunction with calls generated by
- X.LW callrpc() .
- X.LP
- XWarning: the UDP transport mechanism can only deal with
- Xarguments and results less than 8K bytes in length.
- X.NH 2
- XAssigning Program Numbers
- X.LP
- XProgram numbers are assigned in groups of 0x20000000 (536870912)
- Xaccording to the following chart:
- X.BS
- X.LS
- X 0 - 1fffffff defined by sun
- X20000000 - 3fffffff defined by user
- X40000000 - 5fffffff transient
- X60000000 - 7fffffff reserved
- X80000000 - 9fffffff reserved
- Xa0000000 - bfffffff reserved
- Xc0000000 - dfffffff reserved
- Xe0000000 - ffffffff reserved
- X.Lf
- X.BE
- XSun Microsystems administers the first group of numbers,
- Xwhich should be identical for all Sun customers.
- XIf a customer develops an application that might be of general interest,
- Xthat application should be given an assigned number in the first range.
- XThe second group of numbers is reserved for specific customer applications.
- XThis range is intended primarily for debugging new programs.
- XThe third group is reserved for applications that
- Xgenerate program numbers dynamically.
- XThe final groups are reserved for future use, and should not be used.
- X.LP
- XTo register a protocol specification,
- Xsend a request by network mail to
- X.LW sun!rpc ,
- Xor write to:
- X.DS
- XRPC Administrator
- XSun Microsystems
- X2550 Garcia Ave.
- XMountain View, CA 94043
- X.DE
- XPlease include a complete protocol specification,
- Xsimilar to those in this manual for NFS and YP.
- XYou will be given a unique program number in return.
- X.NH 2
- XPassing Arbitrary Data Types
- X.LP
- XIn the previous example, the RPC call passes a single
- X.LW "unsigned long" .
- XRPC can handle arbitrary data structures, regardless of
- Xdifferent machines' byte orders or structure layout conventions,
- Xby always converting them to a network standard called
- X.I "eXternal Data Representation"
- X(XDR) before
- Xsending them over the wire.
- XThe process of converting from a particular machine representation
- Xto XDR format is called
- X.I serializing ,
- Xand the reverse process is called
- X.I deserializing .
- XThe type field parameters of
- X.LW callrpc()
- Xand
- X.LW registerrpc()
- Xcan be a built-in procedure like
- X.LW xdr_u_long()
- Xin the previous example, or a user supplied one.
- XXDR has these built-in type routines:
- X.BS
- X.LS
- Xxdr_int() xdr_u_int() xdr_enum()
- Xxdr_long() xdr_u_long() xdr_bool()
- Xxdr_short() xdr_u_short() xdr_string()
- X.Lf
- X.BE
- XAs an example of a user-defined type routine,
- Xif you wanted to send the structure
- X.BS
- X.LS
- Xstruct simple {
- X int a;
- X short b;
- X} simple;
- X.Lf
- X.BE
- Xthen you would call
- X.LW callrpc()
- Xas
- X.BS
- X.LS
- Xcallrpc(hostname, PROGNUM, VERSNUM, PROCNUM,
- X xdr_simple, &simple ...);
- X.Lf
- X.BE
- Xwhere
- X.LW xdr_simple()
- Xis written as:
- X.BS
- X.LS
- X#include <rpc/rpc.h>
- X.sp.5
- Xxdr_simple(xdrsp, simplep)
- X XDR *xdrsp;
- X struct simple *simplep;
- X{
- X if (!xdr_int(xdrsp, &simplep->a))
- X return (0);
- X if (!xdr_short(xdrsp, &simplep->b))
- X return (0);
- X return (1);
- X}
- X.Lf
- X.BE
- X.LP
- XAn XDR routine returns nonzero (true in the sense of C)
- Xif it completes successfully, and zero otherwise.
- XA complete description of XDR is in the
- X.I "XDR Protocol Specification" ,
- Xso this section only gives a few examples of XDR implementation.
- X.LP
- XIn addition to the built-in primitives,
- Xthere are also the prefabricated building blocks:
- X.BS
- X.LS
- Xxdr_array() xdr_bytes()
- Xxdr_reference() xdr_union()
- X.Lf
- X.BE
- XTo send a variable array of integers,
- Xyou might package them up as a structure like this
- X.BS
- X.LS
- Xstruct varintarr {
- X int *data;
- X int arrlnth;
- X} arr;
- X.Lf
- X.BE
- Xand make an RPC call such as
- X.BS
- X.LS
- Xcallrpc(hostname, PROGNUM, VERSNUM, PROCNUM,
- X xdr_varintarr, &arr...);
- X.Lf
- X.BE
- Xwith
- X.LW xdr_varintarr()
- Xdefined as:
- X.BS
- X.LS
- Xxdr_varintarr(xdrsp, arrp)
- X XDR *xdrsp;
- X struct varintarr *arrp;
- X{
- X xdr_array(xdrsp, &arrp->data, &arrp->arrlnth, MAXLEN,
- X sizeof(int), xdr_int);
- X}
- X.Lf
- X.BE
- XThis routine takes as parameters the XDR handle,
- Xa pointer to the array, a pointer to the size of the array,
- Xthe maximum allowable array size,
- Xthe size of each array element,
- Xand an XDR routine for handling each array element.
- X.LP
- XIf the size of the array is known in advance, then
- Xthe following could also be used to send
- Xout an array of length
- X.LW SIZE :
- X.BS
- X.LS
- Xint intarr[SIZE];
- X.sp.5
- Xxdr_intarr(xdrsp, intarr)
- X XDR *xdrsp;
- X int intarr[];
- X{
- X int i;
- X.sp.5
- X for (i = 0; i < SIZE; i++) {
- X if (!xdr_int(xdrsp, &intarr[i]))
- X return (0);
- X }
- X return (1);
- X}
- X.Lf
- X.BE
- X.LP
- XXDR always converts quantities to 4-byte multiples when deserializing.
- XThus, if either of the examples above involved characters
- Xinstead of integers, each character would occupy 32 bits.
- XThat is the reason for the XDR routine
- X.LW xdr_bytes() ,
- Xwhich is like
- X.LW xdr_array()
- Xexcept that it packs characters;
- X.LW xdr_bytes()
- Xhas four parameters, similar to the first four parameters of
- X.LW xdr_array() .
- XFor null-terminated strings, there is also the
- X.LW xdr_string()
- Xroutine, which is the same as
- X.LW xdr_bytes()
- Xwithout the length parameter.
- XOn serializing it gets the string length from
- X.LW strlen() ,
- Xand on deserializing it creates a null-terminated string.
- X.LP
- XHere is a final example that calls the previously written
- X.LW xdr_simple()
- Xas well as the built-in functions
- X.LW xdr_string()
- Xand
- X.LW xdr_reference() ,
- Xwhich chases pointers:
- X.BS
- X.LS
- Xstruct finalexample {
- X char *string;
- X struct simple *simplep;
- X} finalexample;
- X.sp.5
- Xxdr_finalexample(xdrsp, finalp)
- X XDR *xdrsp;
- X struct finalexample *finalp;
- X{
- X int i;
- X.sp.5
- X if (!xdr_string(xdrsp, &finalp->string, MAXSTRLEN))
- X return (0);
- X if (!xdr_reference(xdrsp, &finalp->simplep,
- X sizeof(struct simple), xdr_simple);
- X return (0);
- X return (1);
- X}
- X.Lf
- X.BE
- X.bp
- X.NH
- XLowest Layer of RPC
- X.LP
- XIn the examples given so far,
- XRPC takes care of many details automatically for you.
- XIn this section, we'll show you how you can change the defaults
- Xby using lower layers of the RPC library.
- XIt is assumed that you are familiar with sockets
- Xand the system calls for dealing with them.
- XIf not, consult the
- X.I "IPC Primer" .
- X.LP
- XThere are several occasions when you may need to use lower layers of RPC.
- XFirst, you may need to use TCP.
- XThe higher layer uses UDP,
- Xwhich restricts RPC calls to 8K bytes of data.
- XUsing TCP permits calls to send long streams of data.
- XFor an example, see section 5.2 below.
- XSecond, you may want to allocate and free memory
- Xwhile serializing or deserializing with XDR routines.
- XThere is no call at the higher level to let you free memory explicitly.
- XFor more explanation, see section 3.2 below.
- XThird, you may need to perform authentication
- Xon either the client or server side,
- Xby supplying credentials or verifying them.
- XSee the explanation in section 4.4 below.
- X.NH 2
- XMore on the Server Side
- X.LP
- XThe server for the
- X.LW nusers
- Xprogram shown below does the same thing as the one using
- X.LW registerrpc()
- Xabove, but is written using a lower layer of the RPC package:
- X.BS
- X.LS no
- X#include <stdio.h>
- X#include <rpc/rpc.h>
- X#include <rpcsvc/rusers.h>
- X.sp.5
- Xmain()
- X{
- X SVCXPRT *transp;
- X int nuser();
- X.sp.5
- X transp = svcudp_create(RPC_ANYSOCK);
- X if (transp == NULL){
- X fprintf(stderr, "can't create an RPC server\en");
- X exit(1);
- X }
- X pmap_unset(RUSERSPROG, RUSERSVERS);
- X if (!svc_register(transp, RUSERSPROG, RUSERSVERS,
- X nuser, IPPROTO_UDP)) {
- X fprintf(stderr, "can't register RUSER service\en");
- X exit(1);
- X }
- X svc_run(); /* never returns */
- X fprintf(stderr, "should never reach this point\en");
- X}
- X.sp.5
- Xnuser(rqstp, tranp)
- X struct svc_req *rqstp;
- X SVCXPRT *transp;
- X{
- X unsigned long nusers;
- X.sp.5
- X switch (rqstp->rq_proc) {
- X case NULLPROC:
- X if (!svc_sendreply(transp, xdr_void, 0)) {
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X }
- X return;
- X case RUSERSPROC_NUM:
- X /*
- X * code here to compute the number of users
- X * and put in variable nusers
- X */
- X if (!svc_sendreply(transp, xdr_u_long, &nusers) {
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X }
- X return;
- X default:
- X svcerr_noproc(transp);
- X return;
- X }
- X}
- X.Lf
- X.BE
- X.LP
- XFirst, the server gets a transport handle, which is used
- Xfor sending out RPC messages.
- X.LW registerrpc()
- Xuses
- X.LW svcudp_create()
- Xto get a UDP handle.
- XIf you require a reliable protocol, call
- X.LW svctcp_create()
- Xinstead.
- XIf the argument to
- X.LW svcudp_create()
- Xis
- X.LW RPC_ANYSOCK ,
- Xthe RPC library creates a socket
- Xon which to send out RPC calls.
- XOtherwise,
- X.LW svcudp_create()
- Xexpects its argument to be a valid socket number.
- XIf you specify your own socket, it can be bound or unbound.
- XIf it is bound to a port by the user, the port numbers of
- X.LW svcudp_create()
- Xand
- X.LW clntudp_create()
- X(the low-level client routine) must match.
- X.LP
- XWhen the user specifies
- X.LW RPC_ANYSOCK
- Xfor a socket or gives an unbound socket,
- Xthe system determines port numbers in the following way:
- Xwhen a server starts up,
- Xit advertises to a port mapper demon on its local machine,
- Xwhich picks a port number for the RPC procedure
- Xif the socket specified to
- X.LW svcudp_create()
- Xisn't already bound.
- XWhen the
- X.LW clntudp_create()
- Xcall is made with an unbound socket,
- Xthe system queries the port mapper on
- Xthe machine to which the call is being made,
- Xand gets the appropriate port number.
- XIf the port mapper is not running
- Xor has no port corresponding to the RPC call,
- Xthe RPC call fails.
- XUsers can make RPC calls
- Xto the port mapper themselves.
- XThe appropriate procedure
- Xnumbers are in the include file
- X.LW <rpc/pmap_prot.h> .
- X.LP
- XAfter creating an
- X.LW SVCXPRT ,
- Xthe next step is to call
- X.LW pmap_unset()
- Xso that if the
- X.LW nusers
- Xserver crashed earlier,
- Xany previous trace of it is erased before restarting.
- XMore precisely,
- X.LW pmap_unset()
- Xerases the entry for
- X.LW RUSERSPROG
- Xfrom the port mapper's tables.
- X.LP
- XFinally, we associate the program number for
- X.LW nusers
- Xwith the procedure
- X.LW nuser() .
- XThe final argument to
- X.LW svc_register()
- Xis normally the protocol being used,
- Xwhich, in this case, is
- X.LW IPPROTO_UDP .
- XNotice that unlike
- X.LW registerrpc() ,
- Xthere are no XDR routines involved
- Xin the registration process.
- XAlso, registration is done on the program,
- Xrather than procedure, level.
- X.LP
- XThe user routine
- X.LW nuser()
- Xmust call and dispatch the appropriate XDR routines
- Xbased on the procedure number.
- XNote that
- Xtwo things are handled by
- X.LW nuser()
- Xthat
- X.LW registerrpc()
- Xhandles automatically.
- XThe first is that procedure
- X.LW NULLPROC
- X(currently zero) returns with no arguments.
- XThis can be used as a simple test
- Xfor detecting if a remote program is running.
- XSecond, there is a check for invalid procedure numbers.
- XIf one is detected,
- X.LW svcerr_noproc()
- Xis called to handle the error.
- X.LP
- XThe user service routine serializes the results and returns
- Xthem to the RPC caller via
- X.LW svc_sendreply() .
- XIts first parameter is the
- X.LW SVCXPRT
- Xhandle, the second is the XDR routine,
- Xand the third is a pointer to the data to be returned.
- XNot illustrated above is how a server
- Xhandles an RPC program that passes data.
- XAs an example, we can add a procedure
- X.LW RUSERSPROC_BOOL ,
- Xwhich has an argument
- X.LW nusers ,
- Xand returns
- X.LW TRUE
- Xor
- X.LW FALSE
- Xdepending on whether there are nusers logged on.
- XIt would look like this:
- X.BS
- X.LS
- Xcase RUSERSPROC_BOOL: {
- X int bool;
- X unsigned nuserquery;
- X.sp.5
- X if (!svc_getargs(transp, xdr_u_int, &nuserquery) {
- X svcerr_decode(transp);
- X return;
- X }
- X /*
- X * code to set nusers = number of users
- X */
- X if (nuserquery == nusers)
- X bool = TRUE;
- X else
- X bool = FALSE;
- X if (!svc_sendreply(transp, xdr_bool, &bool){
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X }
- X return;
- X}
- X.Lf
- X.BE
- X.LP
- XThe relevant routine is
- X.LW svc_getargs() ,
- Xwhich takes an
- X.LW SVCXPRT
- Xhandle, the XDR routine,
- Xand a pointer to where the input is to be placed as arguments.
- X.NH 2
- XMemory Allocation with XDR
- X.LP
- XXDR routines not only do input and output,
- Xthey also do memory allocation.
- XThis is why the second parameter of
- X.LW xdr_array()
- Xis a pointer to an array, rather than the array itself.
- XIf it is
- X.LW NULL ,
- Xthen
- X.LW xdr_array()
- Xallocates space for the array and returns a pointer to it,
- Xputting the size of the array in the third argument.
- XAs an example, consider the following XDR routine
- X.LW xdr_chararr1() ,
- Xwhich deals with a fixed array of bytes with length
- X.LW SIZE :
- X.BS
- X.LS
- Xxdr_chararr1(xdrsp, chararr)
- X XDR *xdrsp;
- X char chararr[];
- X{
- X char *p;
- X int len;
- X.sp.5
- X p = chararr;
- X len = SIZE;
- X return (xdr_bytes(xdrsp, &p, &len, SIZE));
- X}
- X.Lf
- X.BE
- XIt might be called from a server like this,
- X.BS
- X.LS
- Xchar chararr[SIZE];
- X.sp.5
- Xsvc_getargs(transp, xdr_chararr1, chararr);
- X.Lf
- X.BE
- Xwhere
- X.LW chararr
- Xhas already allocated space.
- XIf you want XDR to do the allocation,
- Xyou would have to rewrite this routine in the following way:
- X.BS
- X.LS
- Xxdr_chararr2(xdrsp, chararrp)
- X XDR *xdrsp;
- X char **chararrp;
- X{
- X int len;
- X.sp.5
- X len = SIZE;
- X return (xdr_bytes(xdrsp, charrarrp, &len, SIZE));
- X}
- X.Lf
- X.BE
- XThen the RPC call might look like this:
- X.BS
- X.LS
- Xchar *arrptr;
- X.sp.5
- Xarrptr = NULL;
- Xsvc_getargs(transp, xdr_chararr2, &arrptr);
- X/*
- X * use the result here
- X */
- Xsvc_freeargs(transp, xdr_chararr2, &arrptr);
- X.Lf
- X.BE
- XAfter using the character array, it can be freed with
- X.LW svc_freeargs() .
- XIn the routine
- X.LW xdr_finalexample()
- Xgiven earlier, if
- X.LW finalp->string
- Xwas
- X.LW NULL
- Xin the call
- X.BS
- X.LS
- Xsvc_getargs(transp, xdr_finalexample, &finalp);
- X.Lf
- X.BE
- Xthen
- X.BS
- X.LS
- Xsvc_freeargs(xdrsp, xdr_finalexample, &finalp);
- X.Lf
- X.BE
- Xfrees the array allocated to hold
- X.LW finalp->string ;
- Xotherwise, it frees nothing.
- XThe same is true for
- X.LW finalp->simplep .
- X.LP
- XTo summarize, each XDR routine is responsible
- Xfor serializing, deserializing, and allocating memory.
- XWhen an XDR routine is called from
- X.LW callrpc() ,
- Xthe serializing part is used.
- XWhen called from
- X.LW svc_getargs() ,
- Xthe deserializer is used.
- XAnd when called from
- X.LW svc_freeargs() ,
- Xthe memory deallocator is used.
- XWhen building simple examples like those in this section,
- Xa user doesn't have to worry about the three modes.
- XThe XDR reference manual has examples of more
- Xsophisticated XDR routines that
- Xdetermine which of the three modes they are in
- Xto function correctly.
- X.NH 2
- XThe Calling Side
- X.LP
- XWhen you use
- X.LW callrpc() ,
- Xyou have no control over the RPC delivery
- Xmechanism or the socket used to transport the data.
- XTo illustrate the layer of RPC that lets you adjust these
- Xparameters, consider the following code to call the
- X.LW nusers
- Xservice:
- X.BS
- X.LS no
- X#include <stdio.h>
- X#include <rpc/rpc.h>
- X#include <rpcsvc/rusers.h>
- X#include <sys/socket.h>
- X#include <sys/time.h>
- X#include <netdb.h>
- X.sp.5
- Xmain(argc, argv)
- X int argc;
- X char **argv;
- X{
- X struct hostent *hp;
- X struct timeval pertry_timeout, total_timeout;
- X struct sockaddr_in server_addr;
- X int addrlen, sock = RPC_ANYSOCK;
- X register CLIENT *client;
- X enum clnt_stat clnt_stat;
- X unsigned long nusers;
- X.sp.5
- X if (argc < 2) {
- X fprintf(stderr, "usage: nusers hostname\en");
- X exit(-1);
- X }
- X if ((hp = gethostbyname(argv[1])) == NULL) {
- X fprintf(stderr, "can't get addr for %s\en",argv[1]);
- X exit(-1);
- X }
- X pertry_timeout.tv_sec = 3;
- X pertry_timeout.tv_usec = 0;
- X addrlen = sizeof(struct sockaddr_in);
- X bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr,
- X hp->h_length);
- X server_addr.sin_family = AF_INET;
- X server_addr.sin_port = 0;
- X if ((client = clntudp_create(&server_addr, RUSERSPROG,
- X RUSERSVERS, pertry_timeout, &sock)) == NULL) {
- X clnt_pcreateerror("clntudp_create");
- X exit(-1);
- X }
- X total_timeout.tv_sec = 20;
- X total_timeout.tv_usec = 0;
- X clnt_stat = clnt_call(client, RUSERSPROC_NUM, xdr_void,
- X 0, xdr_u_long, &nusers, total_timeout);
- X if (clnt_stat != RPC_SUCCESS) {
- X clnt_perror(client, "rpc");
- X exit(-1);
- X }
- X clnt_destroy(client);
- X}
- X.Lf
- X.BE
- XThe low-level version of
- X.LW callrpc()
- Xis
- X.LW clnt_call() ,
- Xwhich takes a
- X.LW CLIENT
- Xpointer rather than a host name. The parameters to
- X.LW clnt_call()
- Xare a
- X.LW CLIENT
- Xpointer, the procedure number,
- Xthe XDR routine for serializing the argument,
- Xa pointer to the argument,
- Xthe XDR routine for deserializing the return value,
- Xa pointer to where the return value will be placed,
- Xand the time in seconds to wait for a reply.
- X.LP
- XThe
- X.LW CLIENT
- Xpointer is encoded with the transport mechanism.
- X.LW callrpc()
- Xuses UDP, thus it calls
- X.LW clntudp_create()
- Xto get a
- X.LW CLIENT
- Xpointer. To get TCP (Transport Control Protocol), you would use
- X.LW clnttcp_create() .
- X.LP
- XThe parameters to
- X.LW clntudp_create()
- Xare the server address, the length of the server address,
- Xthe program number, the version number,
- Xa timeout value (between tries), and a pointer to a socket.
- XThe final argument to
- X.LW clnt_call()
- Xis the total time to wait for a response.
- XThus, the number of tries is the
- X.LW clnt_call()
- Xtimeout divided by the
- X.LW clntudp_create()
- Xtimeout.
- X.LP
- XThere is one thing to note when using the
- X.LW clnt_destroy()
- Xcall.
- XIt deallocates any space associated with the
- X.LW CLIENT
- Xhandle, but it does not close the socket associated with it,
- Xwhich was passed as an argument to
- X.LW clntudp_create() .
- XThe reason is that if
- Xthere are multiple client handles using the same socket,
- Xthen it is possible to close one handle
- Xwithout destroying the socket that other handles
- Xare using.
- X.LP
- XTo make a stream connection, the call to
- X.LW clntudp_create()
- Xis replaced with a call to
- X.LW clnttcp_create() .
- X.BS
- X.LS
- Xclnttcp_create(&server_addr, prognum, versnum, &socket,
- X inputsize, outputsize);
- X.Lf
- X.BE
- XThere is no timeout argument; instead, the receive and send buffer
- Xsizes must be specified. When the
- X.LW clnttcp_create()
- Xcall is made, a TCP connection is established.
- XAll RPC calls using that
- X.LW CLIENT
- Xhandle would use this connection.
- XThe server side of an RPC call using TCP has
- X.LW svcudp_create()
- Xreplaced by
- X.LW svctcp_create() .
- X.bp
- X.NH
- XOther RPC Features
- X.LP
- XThis section discusses some other aspects of RPC
- Xthat are occasionally useful.
- X.NH 2
- XSelect on the Server Side
- X.LP
- XSuppose a process is processing RPC requests
- Xwhile performing some other activity.
- XIf the other activity involves periodically updating a data structure,
- Xthe process can set an alarm signal before calling
- X.LW svc_run() .
- XBut if the other activity
- Xinvolves waiting on a a file descriptor, the
- X.LW svc_run()
- Xcall won't work.
- XThe code for
- X.LW svc_run()
- Xis as follows:
- X.BS
- X.LS
- Xvoid
- Xsvc_run()
- X{
- X int readfds;
- X.sp.5
- X for (;;) {
- X readfds = svc_fds;
- X switch (select(32, &readfds, NULL, NULL, NULL)) {
- X.sp.5
- X case -1:
- X if (errno == EINTR)
- X continue;
- X perror("rstat: select");
- X return;
- X case 0:
- X break;
- X default:
- X svc_getreq(readfds);
- X }
- X }
- X}
- X.Lf
- X.BE
- X.LP
- XYou can bypass
- X.LW svc_run()
- Xand call
- X.LW svc_getreq()
- Xyourself.
- XAll you need to know are the file descriptors
- Xof the socket(s) associated with the programs you are waiting on.
- XThus you can have your own
- X.LW select()
- Xthat waits on both the RPC socket,
- Xand your own descriptors.
- X.NH 2
- XBroadcast RPC
- X.LP
- XThe
- X.I portmapper
- Xis a daemon that converts RPC program numbers
- Xinto DARPA protocol port numbers; see
- X.LW portmap (8).
- XYou can't do broadcast RPC without the portmapper,
- X.LW pmap ,
- Xin conjunction with standard RPC protocols.
- XHere are the main differences between
- Xbroadcast RPC and normal RPC calls:
- X.IP 1.
- XNormal RPC expects one answer, whereas
- Xbroadcast RPC expects many answers
- X(one or more answer from each responding machine).
- X.IP 2.
- XBroadcast RPC can only be supported by packet-oriented (connectionless)
- Xtransport protocols like UPD/IP.
- X.IP 3.
- XThe implementation of broadcast RPC
- Xtreats all unsuccessful responses as garbage by filtering them out.
- XThus, if there is a version mismatch between the
- Xbroadcaster and a remote service,
- Xthe user of broadcast RPC never knows.
- X.IP 4.
- XAll broadcast messages are sent to the portmap port.
- XThus, only services that register themselves with their portmapper
- Xare accessible via the broadcast RPC mechanism.
- X.NH 3
- XBroadcast RPC Synopsis
- X.LP
- X.BS
- X.LS
- X#include <rpc/pmap_clnt.h>
- X.sp.5
- Xenum clnt_stat clnt_stat;
- X.sp.5
- Xclnt_stat =
- Xclnt_broadcast(prog, vers, proc, xargs, argsp, xresults,
- X resultsp, eachresult)
- Xu_long prog; /* program number */
- Xu_long vers; /* version number */
- Xu_long proc; /* procedure number */
- Xxdrproc_t xargs; /* xdr routine for args */
- Xcaddr_t argsp; /* pointer to args */
- Xxdrproc_t xresults; /* xdr routine for results */
- Xcaddr_t resultsp; /* pointer to results */
- Xbool_t (*eachresult)(); /* call with each result gotten */
- X.Lf
- X.BE
- XThe procedure
- X.LW eachresult()
- Xis called each time a valid result is obtained.
- XIt returns a boolean that indicates
- Xwhether or not the client wants more responses.
- X.BS
- X.LS
- Xbool_t done;
- X.sp.5
- Xdone =
- Xeachresult(resultsp, raddr)
- Xcaddr_t resultsp;
- Xstruct sockaddr_in *raddr; /* addr of responding machine */
- X.Lf
- X.BE
- XIf
- X.LW done
- Xis
- X.LW TRUE ,
- Xthen broadcasting stops and
- X.LW clnt_broadcast()
- Xreturns successfully.
- XOtherwise, the routine waits for another response.
- XThe request is rebroadcast
- Xafter a few seconds of waiting.
- XIf no responses come back,
- Xthe routine returns with
- X.LW RPC_TIMEDOUT .
- XTo interpret
- X.LW clnt_stat
- Xerrors, feed the error code to
- X.LW clnt_perrno() .
- X.NH 2
- XBatching
- X.LP
- XThe RPC architecture is designed so that clients send a call message,
- Xand wait for servers to reply that the call succeeded.
- XThis implies that clients do not compute
- Xwhile servers are processing a call.
- XThis is inefficient if the client does not want or need
- Xan acknowledgement for every message sent.
- XIt is possible for clients to continue computing
- Xwhile waiting for a response,
- Xusing RPC batch facilities.
- X.LP
- XRPC messages can be placed in a ``pipeline'' of calls
- Xto a desired server; this is called batching.
- XBatching assumes that:
- X1) each RPC call in the pipeline requires no response from the server,
- Xand the server does not send a response message; and
- X2) the pipeline of calls is transported on a reliable
- Xbyte stream transport such as TCP/IP.
- XSince the server does not respond to every call,
- Xthe client can generate new calls in parallel
- Xwith the server executing previous calls.
- XFurthermore, the TCP/IP implementation can buffer up
- Xmany call messages, and send them to the server in one
- X.LW write()
- Xsystem call. This overlapped execution
- Xgreatly decreases the interprocess communication overhead of
- Xthe client and server processes,
- Xand the total elapsed time of a series of calls.
- X.LP
- XSince the batched calls are buffered,
- Xthe client should eventually do a legitimate call
- Xin order to flush the pipeline.
- X.LP
- XA contrived example of batching follows.
- XAssume a string rendering service (like a window system)
- Xhas two similar calls: one renders a string and returns void results,
- Xwhile the other renders a string and remains silent.
- XThe service (using the TCP/IP transport) may look like:
- X.BS
- X.LS no
- X#include <stdio.h>
- X#include <rpc/rpc.h>
- X#include <rpcsvc/windows.h>
- X.sp.5
- Xvoid windowdispatch();
- X.sp.5
- Xmain()
- X{
- X SVCXPRT *transp;
- X.sp.5
- X transp = svctcp_create(RPC_ANYSOCK, 0, 0);
- X if (transp == NULL){
- X fprintf(stderr, "can't create an RPC server\en");
- X exit(1);
- X }
- X pmap_unset(WINDOWPROG, WINDOWVERS);
- X if (!svc_register(transp, WINDOWPROG, WINDOWVERS,
- X windowdispatch, IPPROTO_TCP)) {
- X fprintf(stderr, "can't register WINDOW service\en");
- X exit(1);
- X }
- X svc_run(); /* never returns */
- X fprintf(stderr, "should never reach this point\en");
- X}
- X.sp.5
- Xvoid
- Xwindowdispatch(rqstp, transp)
- X struct svc_req *rqstp;
- X SVCXPRT *transp;
- X{
- X char *s = NULL;
- X.sp.5
- X switch (rqstp->rq_proc) {
- X case NULLPROC:
- X if (!svc_sendreply(transp, xdr_void, 0)) {
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X }
- X return;
- X case RENDERSTRING:
- X if (!svc_getargs(transp, xdr_wrapstring, &s)) {
- X fprintf(stderr, "can't decode arguments\en");
- X /*
- X * tell caller he screwed up
- X */
- X svcerr_decode(transp);
- X break;
- X }
- X /*
- X * call here to render the string s
- X */
- X if (!svc_sendreply(transp, xdr_void, NULL)) {
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X }
- X break;
- X case RENDERSTRING_BATCHED:
- X if (!svc_getargs(transp, xdr_wrapstring, &s)) {
- X fprintf(stderr, "can't decode arguments\en");
- X /*
- X * we are silent in the face of protocol errors
- X */
- X break;
- X }
- X /*
- X * call here to render string s, but send no reply!
- X */
- X break;
- X default:
- X svcerr_noproc(transp);
- X return;
- X }
- X /*
- X * now free string allocated while decoding arguments
- X */
- X svc_freeargs(transp, xdr_wrapstring, &s);
- X}
- X.Lf
- X.BE
- XOf course the service could have one procedure
- Xthat takes the string and a boolean
- Xto indicate whether or not the procedure should respond.
- X.LP
- XIn order for a client to take advantage of batching,
- Xthe client must perform RPC calls on a TCP-based transport
- Xand the actual calls must have the following attributes:
- X1) the result's XDR routine must be zero
- X.LW NULL ), (
- Xand 2) the RPC call's timeout must be zero.
- X.LP
- XHere is an example of a client that uses batching
- Xto render a bunch of strings;
- Xthe batching is flushed when the client gets a null string:
- X.BS
- X.LS no
- X#include <stdio.h>
- X#include <rpc/rpc.h>
- X#include <rpcsvc/windows.h>
- X#include <sys/socket.h>
- X#include <sys/time.h>
- X#include <netdb.h>
- X.sp.5
- Xmain(argc, argv)
- X int argc;
- X char **argv;
- X{
- X struct hostent *hp;
- X struct timeval pertry_timeout, total_timeout;
- X struct sockaddr_in server_addr;
- X int addrlen, sock = RPC_ANYSOCK;
- X register CLIENT *client;
- X enum clnt_stat clnt_stat;
- X char buf[1000], *s = buf;
- X.sp.5
- X /* initial as in example 3.3
- X */
- X if ((client = clnttcp_create(&server_addr,
- X WINDOWPROG, WINDOWVERS, &sock, 0, 0)) == NULL) {
- X perror("clnttcp_create");
- X exit(-1);
- X }
- X total_timeout.tv_sec = 0;
- X total_timeout.tv_usec = 0;
- X while (scanf("%s", s) != EOF) {
- X clnt_stat = clnt_call(client, RENDERSTRING_BATCHED,
- X xdr_wrapstring, &s, NULL, NULL, total_timeout);
- X if (clnt_stat != RPC_SUCCESS) {
- X clnt_perror(client, "batched rpc");
- X exit(-1);
- X }
- X }
- X /* now flush the pipeline
- X */
- X total_timeout.tv_sec = 20;
- X clnt_stat = clnt_call(client, NULLPROC, xdr_void, NULL,
- X xdr_void, NULL, total_timeout);
- X if (clnt_stat != RPC_SUCCESS) {
- X clnt_perror(client, "rpc");
- X exit(-1);
- X }
- X clnt_destroy(client);
- X}
- X.Lf
- X.BE
- XSince the server sends no message,
- Xthe clients cannot be notified of any of the failures that may occur.
- XTherefore, clients are on their own when it comes to handling errors.
- X.LP
- XThe above example was completed to render
- Xall of the (2000) lines in the file
- X.I /etc/termcap .
- XThe rendering service did nothing but throw the lines away.
- XThe example was run in the following four configurations:
- X1) machine to itself, regular RPC;
- X2) machine to itself, batched RPC;
- X3) machine to another, regular RPC; and
- X4) machine to another, batched RPC.
- XThe results are as follows:
- X1) 50 seconds;
- X2) 16 seconds;
- X3) 52 seconds;
- X4) 10 seconds.
- XRunning
- X.LW fscanf()
- Xon
- X.I /etc/termcap
- Xonly requires six seconds.
- XThese timings show the advantage of protocols
- Xthat allow for overlapped execution,
- Xthough these protocols are often hard to design.
- X.NH 2
- XAuthentication
- X.LP
- XIn the examples presented so far,
- Xthe caller never identified itself to the server,
- Xand the server never required an ID from the caller.
- XClearly, some network services, such as a network filesystem,
- Xrequire stronger security than what has been presented so far.
- X.LP
- XIn reality, every RPC call is authenticated by
- Xthe RPC package on the server, and similarly,
- Xthe RPC client package generates and sends authentication parameters.
- XJust as different transports (TCP/IP or UDP/IP)
- Xcan be used when creating RPC clients and servers,
- Xdifferent forms of authentication can be associated with RPC clients;
- Xthe default authentication type used as a default is type
- X.I none .
- X.LP
- XThe authentication subsystem of the RPC package is open ended.
- XThat is, numerous types of authentication are easy to support.
- XHowever, this section deals only with
- X.I unix
- Xtype authentication, which besides
- X.I none
- Xis the only supported type.
- X.NH 3
- XThe Client Side
- X.LP
- XWhen a caller creates a new RPC client handle as in:
- X.BS
- X.LS
- Xclnt = clntudp_create(address, prognum, versnum,
- X wait, sockp)
- X.Lf
- X.BE
- Xthe appropriate transport instance defaults
- Xthe associate authentication handle to be
- X.BS
- X.LS
- Xclnt->cl_auth = authnone_create();
- X.Lf
- X.BE
- XThe RPC client can choose to use
- X.I unix
- Xstyle authentication by setting
- X.LW clnt->cl_auth
- Xafter creating the RPC client handle:
- X.BS
- X.LS
- Xclnt->cl_auth = authunix_create_default();
- X.Lf
- X.BE
- XThis causes each RPC call associated with
- X.LW clnt
- Xto carry with it the following authentication credentials structure:
- X.BS
- X.LS
- X/*
- X * Unix style credentials.
- X */
- Xstruct authunix_parms {
- X u_long aup_time; /* credentials creation time */
- X char *aup_machname; /* host name where client is */
- X int aup_uid; /* client's UNIX effective uid */
- X int aup_gid; /* client's current group id */
- X u_int aup_len; /* element length of aup_gids */
- X int *aup_gids; /* array of groups user is in */
- X};
- X.Lf
- X.BE
- XThese fields are set by
- X.LW authunix_create_default()
- Xby invoking the appropriate system calls.
- XSince the RPC user created this new style of authentication,
- Xthe user is responsible for destroying it with:
- X.BS
- X.LS
- Xauth_destroy(clnt->cl_auth);
- X.Lf
- X.BE
- XThis should be done in all cases, to conserve memory.
- X.NH 3
- XThe Server Side
- X.LP
- XService implementors have a harder time dealing with authentication issues
- Xsince the RPC package passes the service dispatch routine a request
- Xthat has an arbitrary authentication style associated with it.
- XConsider the fields of a request handle passed to a service dispatch routine:
- X.BS
- X.LS
- X/*
- X * An RPC Service request
- X */
- Xstruct svc_req {
- X u_long rq_prog; /* service program number */
- X u_long rq_vers; /* service protocol vers num */
- X u_long rq_proc; /* desired procedure number */
- X struct opaque_auth
- X rq_cred; /* raw credentials from wire */
- X caddr_t rq_clntcred; /* credentials (read only) */
- X};
- X.Lf
- X.BE
- XThe
- X.LW rq_cred
- Xis mostly opaque, except for one field of interest:
- Xthe style of authentication credentials:
- X.BS
- X.LS
- X/*
- X * Authentication info. Mostly opaque to the programmer.
- X */
- Xstruct opaque_auth {
- X enum_t oa_flavor; /* style of credentials */
- X caddr_t oa_base; /* address of more auth stuff */
- X u_int oa_length; /* not to exceed MAX_AUTH_BYTES */
- X};
- X.Lf
- X.BE
- XThe RPC package guarantees the following
- Xto the service dispatch routine:
- X.IP 1.
- XThat the request's
- X.LW rq_cred
- Xis well formed. Thus the service implementor may inspect the request's
- X.LW rq_cred.oa_flavor
- Xto determine which style of authentication the caller used.
- XThe service implementor may also wish to inspect the other fields of
- X.LW rq_cred
- Xif the style is not one of the styles supported by the RPC package.
- X.IP 2.
- XThat the request's
- X.LW rq_clntcred
- Xfield is either
- X.LW NULL
- Xor points to a well formed structure
- Xthat corresponds to a supported style of authentication credentials.
- XRemember that only
- X.I unix
- Xstyle is currently supported, so (currently)
- X.LW rq_clntcred
- Xcould be cast to a pointer to an
- X.LW authunix_parms
- Xstructure. If
- X.LW rq_clntcred
- Xis
- X.LW NULL ,
- Xthe service implementor may wish to inspect the other (opaque) fileds of
- X.LW rq_cred
- Xin case the service knows about a new type of authentication
- Xthat the RPC package does not know about.
- X.LP
- XOur remote users service example can be extended so that
- Xit computes results for all users except UID 16:
- X.BS
- X.LS no
- Xnuser(rqstp, tranp)
- X struct svc_req *rqstp;
- X SVCXPRT *transp;
- X{
- X struct authunix_parms *unix_cred;
- X int uid;
- X unsigned long nusers;
- X.sp.5
- X /*
- X * we don't care about authentication for null proc
- X */
- X if (rqstp->rq_proc == NULLPROC) {
- X if (!svc_sendreply(transp, xdr_void, 0)) {
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X }
- X return;
- X }
- X /*
- X * now get the uid
- X */
- X switch (rqstp->rq_cred.oa_flavor) {
- X case AUTH_UNIX:
- X unix_cred = (struct authunix_parms *)rqstp->rq_clntcred;
- X uid = unix_cred->aup_uid;
- X break;
- X case AUTH_NULL:
- X default:
- X svcerr_weakauth(transp);
- X return;
- X }
- X switch (rqstp->rq_proc) {
- X case RUSERSPROC_NUM:
- X /*
- X * make sure caller is allowed to call this proc
- X */
- X if (uid == 16) {
- X svcerr_systemerr(transp);
- X return;
- X }
- X /*
- X * code here to compute the number of users
- X * and put in variable nusers
- X */
- X if (!svc_sendreply(transp, xdr_u_long, &nusers) {
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X }
- X return;
- X default:
- X svcerr_noproc(transp);
- X return;
- X }
- X}
- X.Lf
- X.BE
- XA few things should be noted here.
- XFirst, it is customary not to check
- Xthe authentication parameters associated with the
- X.LW NULLPROC
- X(procedure number zero).
- XSecond, if the authentication parameter's type is not suitable
- Xfor your service, you should call
- X.LW svcerr_weakauth() .
- XAnd finally, the service protocol itself should return status
- Xfor access denied; in the case of our example, the protocol
- Xdoes not have such a status, so we call the service primitive
- X.LW svcerr_systemerr()
- Xinstead.
- X.LP
- XThe last point underscores the relation between
- Xthe RPC authentication package and the services;
- XRPC deals only with authentication and not with
- Xindividual services' access control.
- XThe services themselves must implement their own access control policies
- Xand reflect these policies as return statuses in their protocols.
- X.NH 2
- XUsing Inetd
- X.LP
- XAn RPC server can be started from
- X.LW inetd .
- XThe only difference
- Xfrom the usual code is that
- X.LW svcudp_create()
- Xshould be called as
- X.BS
- X.LS
- Xtransp = svcudp_create(0);
- X.Lf
- X.BE
- Xsince
- X.LW inet
- Xpasses a socket as file descriptor 0.
- XAlso,
- X.LW svc_register()
- Xshould be called as
- X.BS
- X.LS
- Xsvc_register(transp, PROGNUM, VERSNUM, service, 0);
- X.Lf
- X.BE
- Xwith the final flag as 0,
- Xsince the program would already be registered by
- X.LW inetd .
- XRemember that if you want to exit
- Xfrom the server process and return control to
- X.LW inet ,
- Xyou need to explicitly exit, since
- X.LW svc_run()
- Xnever returns.
- X.LP
- XThe format of entries in /etc/servers for RPC services is
- X.BS
- X.LS
- Xrpc udp \fIserver \0program \0version\fP
- X.Lf
- X.BE
- Xwhere
- X.I server
- Xis the C code implementing the server,
- Xand
- X.I program
- Xand
- X.I version
- Xare the program and version numbers of the service.
- XThe key word
- X.LW udp
- Xcan be replaced by
- X.LW tcp
- Xfor TCP-based RPC services.
- X.LP
- XIf the same program handles multiple versions,
- Xthen the version number can be a range,
- Xas in this example:
- X.BS
- X.LS
- Xrpc udp /usr/etc/rstatd 100001 1-2
- X.Lf
- X.BE
- X.bp
- X.NH
- XMore Examples
- X.NH 2
- XVersions
- X.LP
- XBy convention, the first version number of program
- X.LW PROG
- Xis
- X.LW PROGVERS_ORIG
- Xand the most recent version is
- X.LW PROGVERS .
- XSuppose there is a new version of the
- X.LW user
- Xprogram that returns an
- X.LW "unsigned short"
- Xrather than a
- X.LW long .
- XIf we name this version
- X.LW RUSERSVERS_SHORT ,
- Xthen a server that wants to support both versions
- Xwould do a double register.
- X.BS
- X.LS
- Xif (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG,
- X nuser, IPPROTO_TCP)) {
- X fprintf(stderr, "can't register RUSER service\en");
- X exit(1);
- X}
- Xif (!svc_register(transp, RUSERSPROG, RUSERSVERS_SHORT,
- X nuser, IPPROTO_TCP)) {
- X fprintf(stderr, "can't register RUSER service\en");
- X exit(1);
- X}
- X.Lf
- X.BE
- XBoth versions can be handled by the same C procedure:
- X.BS
- X.LS no
- Xnuser(rqstp, tranp)
- X struct svc_req *rqstp;
- X SVCXPRT *transp;
- X{
- X unsigned long nusers;
- X unsigned short nusers2
- X.sp.5
- X switch (rqstp->rq_proc) {
- X case NULLPROC:
- X if (!svc_sendreply(transp, xdr_void, 0)) {
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X }
- X return;
- X case RUSERSPROC_NUM:
- X /*
- X * code here to compute the number of users
- X * and put in variable nusers
- X */
- X nusers2 = nusers;
- X if (rqstp->rq_vers != RUSERSVERS_ORIG)
- X return;
- X if (!svc_sendreply(transp, xdr_u_long, &nusers) {
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X } else
- X if (!svc_sendreply(transp, xdr_u_short, &nusers2) {
- X fprintf(stderr, "can't reply to RPC call\en");
- X exit(1);
- X }
- X return;
- X default:
- X svcerr_noproc(transp);
- X return;
- X }
- X}
- X.Lf
- X.BE
- X.NH 2
- XTCP
- X.LP
- XHere is an example that is essentially
- X.LW rcp .
- XThe initiator of the RPC
- X.LW snd()
- Xcall takes its standard input and sends it to the server
- X.LW rcv() ,
- Xwhich prints it on standard output.
- XThe RPC call uses TCP.
- XThis also illustrates an XDR procedure that behaves differently
- Xon serialization than on deserialization.
- X.BS
- X.LS no
- X/*
- X * The xdr routine:
- X * on decode, read from wire, write onto fp
- X * on encode, read from fp, write onto wire
- X */
- X#include <stdio.h>
- X#include <rpc/rpc.h>
- X.sp.5
- Xxdr_rcp(xdrs, fp)
- X XDR *xdrs;
- X FILE *fp;
- X{
- X unsigned long size;
- X char buf[BUFSIZ], *p;
- X.sp.5
- X if (xdrs->x_op == XDR_FREE)/* nothing to free */
- X return 1;
- X while (1) {
- X if (xdrs->x_op == XDR_ENCODE) {
- X if ((size = fread(buf, sizeof(char), BUFSIZ,
- X fp)) == 0 && ferror(fp)) {
- X fprintf(stderr, "can't fread\en");
- X exit(1);
- X }
- X }
- X p = buf;
- X if (!xdr_bytes(xdrs, &p, &size, BUFSIZ))
- X return 0;
- X if (size == 0)
- X return 1;
- X if (xdrs->x_op == XDR_DECODE) {
- X if (fwrite(buf, sizeof(char), size,
- X fp) != size) {
- X fprintf(stderr, "can't fwrite\en");
- X exit(1);
- X }
- X }
- X }
- X}
- X.sp.5
- X/*
- X * The sender routines
- X */
- X#include <stdio.h>
- X#include <netdb.h>
- X#include <rpc/rpc.h>
- X#include <sys/socket.h>
- X#include <sys/time.h>
- X.sp.5
- Xmain(argc, argv)
- X int argc;
- X char **argv;
- X{
- X int err;
- X.sp.5
- X if (argc < 2) {
- X fprintf(stderr, "usage: %s servername\en", argv[0]);
- X exit(-1);
- X }
- X if ((err = callrpctcp(argv[1], RCPPROG, RCPPROC_FP,
- X RCPVERS, xdr_rcp, stdin, xdr_void, 0) != 0)) {
- X clnt_perrno(err);
- X fprintf(stderr, "can't make RPC call\en");
- X exit(1);
- X }
- X}
- X.sp.5
- Xcallrpctcp(host, prognum, procnum, versnum,
- X inproc, in, outproc, out)
- X char *host, *in, *out;
- X xdrproc_t inproc, outproc;
- X{
- X struct sockaddr_in server_addr;
- X int socket = RPC_ANYSOCK;
- X enum clnt_stat clnt_stat;
- X struct hostent *hp;
- X register CLIENT *client;
- X struct timeval total_timeout;
- X.sp.5
- X if ((hp = gethostbyname(host)) == NULL) {
- X fprintf(stderr, "can't get addr for '%s'\en", host);
- X exit(-1);
- X }
- X bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr,
- X hp->h_length);
- X server_addr.sin_family = AF_INET;
- X server_addr.sin_port = 0;
- X if ((client = clnttcp_create(&server_addr, prognum,
- X versnum, &socket, BUFSIZ, BUFSIZ)) == NULL) {
- X perror("rpctcp_create");
- X exit(-1);
- X }
- X total_timeout.tv_sec = 20;
- X total_timeout.tv_usec = 0;
- X clnt_stat = clnt_call(client, procnum,
- X inproc, in, outproc, out, total_timeout);
- X clnt_destroy(client)
- X return (int)clnt_stat;
- X}
- X.sp.5
- X/*
- X * The receiving routines
- X */
- X#include <stdio.h>
- X#include <rpc/rpc.h>
- X.sp.5
- Xmain()
- X{
- X register SVCXPRT *transp;
- X.sp.5
- X if ((transp = svctcp_create(RPC_ANYSOCK,
- X BUFSIZ, BUFSIZ)) == NULL) {
- X fprintf("svctcp_create: error\en");
- X exit(1);
- X }
- X pmap_unset(RCPPROG, RCPVERS);
- X if (!svc_register(transp,
- X RCPPROG, RCPVERS, rcp_service, IPPROTO_TCP)) {
- X fprintf(stderr, "svc_register: error\en");
- X exit(1);
- X }
- X svc_run(); /* never returns */
- X fprintf(stderr, "svc_run should never return\en");
- X}
- X.sp.5
- Xrcp_service(rqstp, transp)
- X register struct svc_req *rqstp;
- X register SVCXPRT *transp;
- X{
- X switch (rqstp->rq_proc) {
- X case NULLPROC:
- X if (svc_sendreply(transp, xdr_void, 0) == 0) {
- X fprintf(stderr, "err: rcp_service");
- X exit(1);
- X }
- X return;
- X case RCPPROC_FP:
- X if (!svc_getargs(transp, xdr_rcp, stdout)) {
- X svcerr_decode(transp);
- X return;
- X }
- X if (!svc_sendreply(transp, xdr_void, 0)) {
- X fprintf(stderr, "can't reply\en");
- X return;
- X }
- X exit(0);
- X default:
- X svcerr_noproc(transp);
- X return;
- X }
- X}
- X.Lf
- X.BE
- X.NH 2
- XCallback Procedures
- X.LP
- XOccasionally, it is useful to have a server become a client,
- Xand make an RPC call back the process which is its client.
- XAn example is remote debugging,
- Xwhere the client is a window system program,
- Xand the server is a debugger running on the remote machine.
- XMost of the time,
- Xthe user clicks a mouse button at the debugging window,
- Xwhich converts this to a debugger command,
- Xand then makes an RPC call to the server
- X(where the debugger is actually running),
- Xtelling it to execute that command.
- XHowever, when the debugger hits a breakpoint, the roles are reversed,
- Xand the debugger wants to make an rpc call to the window program,
- Xso that it can inform the user that a breakpoint has been reached.
- SHAR_EOF
- if test 49365 -ne "`wc -c < 'rpc/doc/rpc.prog.p1'`"
- then
- echo shar: "error transmitting 'rpc/doc/rpc.prog.p1'" '(should have been 49365 characters)'
- fi
- chmod 444 'rpc/doc/rpc.prog.p1'
- fi
- exit 0
- # End of shell archive
-
-